home *** CD-ROM | disk | FTP | other *** search
/ C/C++ Users Group Library 1996 July / C-C++ Users Group Library July 1996.iso / vol_400 / 432_01 / ptmid3 / ptmidsav.c < prev    next >
C/C++ Source or Header  |  1994-07-02  |  15KB  |  475 lines

  1. /*
  2.  * ptmidsav.c: MOD-format saver module for ptmid. Takes a structure
  3.  * representing a tune and creates a file which contains that tune.
  4.  *
  5.  * Author: Andrew Scott  (c)opyright 1994
  6.  *
  7.  * Date: 17/11/1993 ver 0.0
  8.  *       8/1/1994   ver 0.1
  9.  *       11/2/1994  ver 0.2
  10.  */
  11.  
  12. #include <stdio.h>
  13. #include <stdlib.h>
  14. #include <string.h>
  15. #include <math.h>
  16. #include "ptmid.h"
  17. #include "samples.h"
  18.  
  19. /**
  20.  ** The midiperiod array allows quick conversion between MIDI note
  21.  ** values and Protracker note values. All "out-of-bounds" values
  22.  ** are given closest legal value.
  23.  **/
  24. unsigned midiperiod[128] = {
  25.   0,   1712,1712,1712,1712,1712,1712,1712,1712,1712,1712,1712,
  26.   1712,1712,1712,1712,1712,1712,1712,1712,1712,1712,1712,1712,
  27.   1712,1712,1712,1712,1712,1712,1712,1712,1712,1712,1712,1712,
  28.   1712,1616,1525,1440,1357,1281,1209,1141,1077,1017,961, 907,
  29.   856, 808, 762, 720, 678, 640, 604, 570, 538, 508, 480, 453,
  30.   428, 404, 381, 360, 339, 320, 302, 285, 269, 254, 240, 226,
  31.   214, 202, 190, 180, 170, 160, 151, 143, 135, 127, 120, 113,
  32.   107, 101, 95,  90,  85,  80,  76,  71,  67,  64,  60,  57,
  33.   57,  57,  57,  57,  57,  57,  57,  57,  57,  57,  57,  57,
  34.   57,  57,  57,  57,  57,  57,  57,  57,  57,  57,  57,  57,
  35.   57,  57,  57,  57,  57,  57,  57,  57
  36.   };
  37.  
  38. /*
  39.  * PutblanksPfile: Outputs as many null characters to a given file as
  40.  * is desired (max. 131). Inelegant solution.
  41.  */
  42. void PutblanksPfile(FILE *pfile, int cch)
  43. {
  44.     if (cch)
  45.         fwrite("\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
  46.             "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
  47.             "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
  48.             "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
  49.             "\0\0", 1, cch, pfile);
  50. }
  51.  
  52. /*
  53.  * PutPfileSz: Outputs a string to a given file and pads it to the correct
  54.  * length by outputting null characters if necessary.
  55.  */
  56. void PutPfileSz(FILE *pfile, Sz szT, int cch)
  57. {
  58.     while (*szT && cch) {
  59.         putc(*(szT++), pfile);
  60.         cch--;
  61.     }
  62.     PutblanksPfile(pfile, cch);
  63. }
  64.  
  65. /*
  66.  * WritePfile: Writes division information (sample, pitch, effect) to
  67.  * given file in the format specified by wModfmt.
  68.  *
  69.  * date: 2/7/1994 - added multi-format support
  70.  *       3/7/1994 - now aborts on write error
  71.  */
  72. void WritePfile(FILE *pfile, unsigned bSam, unsigned wPit, unsigned wEff)
  73. {
  74.   static int cNote = -1, irgchPos, Buffsiz;
  75.   static char *pchBuff;
  76.  
  77.   if (-1 == cNote) {
  78.     irgchPos = 0;
  79.     if (1 == wModfmt)
  80.       pchBuff = (char *) malloc(cNote = Buffsiz = 4 * wMaxchan * DIVSPERPAT);
  81.     else
  82.       pchBuff = (char *) malloc(cNote = Buffsiz = 3 * wMaxchan * DIVSPERPAT);
  83.   }
  84.   switch (wModfmt) {
  85.     case 1:
  86.       wPit = midiperiod[wPit];
  87.       pchBuff[0 + irgchPos] = (bSam & 0xF0) | (wPit >> 8);
  88.       pchBuff[1 + irgchPos] = wPit & 0xFF;
  89.       pchBuff[2 + irgchPos] = ((bSam & 0x0F) << 4) | (wEff >> 8);
  90.       pchBuff[3 + irgchPos] = wEff & 0xFF;
  91.       cNote -= 4;
  92.       irgchPos += 4;
  93.       break;
  94.     case 2:
  95.       if (0 < wPit)
  96.         wPit = wPit - 36;
  97.       pchBuff[0 + irgchPos] = ((bSam & 0x30) >> 4) | (wPit << 2);
  98.       pchBuff[1 + irgchPos] = ((bSam & 0x0F) << 4) | (wEff >> 8);
  99.       pchBuff[2 + irgchPos] = wEff & 0xFF;
  100.       cNote -= 3;
  101.       if ((irgchPos += DIVSPERPAT * 3) >= Buffsiz)
  102.         irgchPos -= Buffsiz - 3;
  103.       break;
  104.   }
  105.   if (0 == cNote) {
  106.     if (0 == fwrite(pchBuff, Buffsiz, 1, pfile)) {
  107.       ERROR;
  108.       exit(1);
  109.     }
  110.     irgchPos = 0;
  111.     cNote = Buffsiz;
  112.   }
  113. }
  114.  
  115. /*
  116.  * PeiNextPtune: Given a pointer to a tune, will start using it if not
  117.  * already using one. Will return next event in quanta, else NULL.
  118.  * If it gets to the end of the tune, will set flag to false and will
  119.  * wait to be given a new tune.
  120.  */
  121. EI *PeiNextPtune(Tune *ptuneMain, int *pf)
  122. {
  123.     static Tune *ptune = NULL;
  124.     static unsigned long quant;
  125.     EI *pei;
  126.  
  127.     if (NULL == ptune) { /** If first time called **/
  128.         if (NULL == ptuneMain) /** If no tune given, quit **/
  129.             return NULL;
  130.         *pf = 1;
  131.         quant = 0;
  132.         ptune = ptuneMain; /** Initialize tune pointer to start of tune **/
  133.     } else /** Else **/
  134.         quant++; /** Advance along tune 1 quantize fraction **/
  135.     if (quant < ptune->count) /** If haven't reached next event **/
  136.         return NULL; /** return nothing-happening **/
  137.  
  138.     pei = ptune->pei; /** Otherwise note next event **/
  139.     if ((ptune = ptune->ptune) == NULL) /** If no more events **/
  140.         *pf = 0; /** register end of tune **/
  141.     return pei; /** Return that event **/
  142. }
  143.  
  144. /*
  145.  * Convqpm: Converts a given qpm number into a double tempo thingy.
  146.  */
  147. void Convqpm(unsigned qpm, int rgbTempo[2], int ticks)
  148. {
  149.     if (792 / ticks <= qpm && 6144 / ticks - 1 >= qpm) {
  150.         rgbTempo[0] = ticks; /** If can use current ticks/div, do so **/
  151.         rgbTempo[1] = qpm * ticks / 24;
  152.     } else if (33 > qpm) /** Else if qpm is very small **/
  153.         if (26 > qpm) { /** approximate it **/
  154.             rgbTempo[0] = 31;
  155.             rgbTempo[1] = 33;
  156.         } else {
  157.             rgbTempo[0] = 31;
  158.             rgbTempo[1] = qpm * 31 / 24;
  159.         }
  160.     else if (6144 <= qpm) { /** Else if qpm is very big **/
  161.         rgbTempo[0] = 1; /** approximate it too **/
  162.         rgbTempo[1] = 255;
  163.     } else { /** Else look for closest fraction **/
  164.         int j, k, kMax;
  165.         double ratio, junk;
  166.  
  167.         ratio = qpm / 24.0;
  168.         j = k = 791 / qpm + 1; /** I hope these constraints are Ok **/
  169.         kMax = 6143 / qpm;
  170.         while (k++ < kMax)
  171.             if (fabs(modf(ratio * k, &junk) - 0.5) >
  172.              fabs(modf(ratio * j, &junk) - 0.5))
  173.                 j = k;
  174.         rgbTempo[0] = j;
  175.         rgbTempo[1] = j * ratio + 0.5;
  176.     }
  177. }
  178.  
  179. /*
  180.  * PutpatternsPtunePfile: Given an output file and a tune, will output the
  181.  * tune as standard Protracker patterns. wMaxchan determines number of
  182.  * channels per division. wPatmax determines maximum number of patterns
  183.  * to write before terminating.
  184.  *
  185.  * date: 3/7/1994 - quiet samples now play sample 0 rather than sample 31
  186.  */
  187. int PutpatternsPtunePfile(Tune *ptune, FILE *pfile)
  188. {
  189.   int iT, iT2, wPat = 0, cDiv, ipw, ipwMax, fGoing;
  190.   unsigned *pwLen, pwNote[3 * MAXCHANS], rgbTempo[2] = {6,125};
  191.   unsigned wNewtempo = 120;
  192.     unsigned long cDev = 0;
  193.     EI *pei;
  194.  
  195.   pwLen = (unsigned *) calloc(MAXCHANS, sizeof(unsigned));
  196.   pei = PeiNextPtune(ptune, &fGoing); /** Get first event **/
  197.   ipw = wMaxchan;
  198.   ipwMax = wMaxchan * 3;
  199.     for (wPat = 0; fGoing; wPat++) { /** Loop until told to stop **/
  200.         if (wPat == wPatmax) { /** If pattern limit reached, stop **/
  201.             Error("Warning -- Pattern limit %d reached. Aborting!", wPatmax);
  202.             break;
  203.         }
  204.         for (cDiv = 64; cDiv--; ) { /** For each division in a pattern **/
  205.             memset(pwNote, 0, ipwMax * sizeof(unsigned)); /** Clear next notes **/
  206.  
  207.       for (iT = wMaxchan; iT--; ) /** With any currently playing notes **/
  208.                 if (pwLen[iT])
  209.                     if (0 == --pwLen[iT]) { /** Check if just stopped **/
  210.             pwNote[iT2 = iT * 3] = 0; /** Yes.. store quiet command **/
  211.                         pwNote[iT2 + 2] = 0xC00;
  212.                     }
  213.  
  214.             if (fGoing) { /** If still going in the tune **/
  215.         for (; NULL != pei; pei = pei->pei) /** For each event at this position **/
  216.           if (-1 != pei->pitch) { /** If note-event **/
  217.             if (-2 != pei->pitch) { /** which is valid **/
  218.  
  219.               iT = ipwMax - 1; /*** Find channel to stick note ***/
  220.               for (; 0 < iT && 0xC00 != pwNote[iT]; iT -= 3);
  221.               if (0 > iT) {
  222.                 for (iT = ipw; iT--; )
  223.                   if (0 == pwLen[iT])
  224.                     break;
  225.                 if (0 > iT) {
  226.                   for (iT = wMaxchan; iT-- > ipw; )
  227.                     if (0 == pwLen[iT])
  228.                       break;
  229.                   if (0 > iT) {
  230.                     iT2 = (unsigned) pei->effect / 2;
  231.                     for (iT = wMaxchan; iT--; )
  232.                       if (iT2 <= pwLen[iT])
  233.                         break;
  234.                     if (0 > iT) {
  235.                       cDev++;
  236.                       continue;
  237.                     }
  238.                   }
  239.                 }
  240.                 if (--ipw < 0)
  241.                   ipw = wMaxchan - 1;
  242.                 iT = iT * 3 + 2;
  243.               }
  244.  
  245.               pwNote[iT -